
package cfar.ff.data;


import cfar.ff.fun.F1;
import cfar.ff.interfaces.Applicative;
import cfar.ff.interfaces.Empty;
import cfar.ff.interfaces.From;
import cfar.ff.interfaces.Functor;
import cfar.ff.interfaces.Monad;
import cfar.ff.interfaces.Pointed;

import java.io.Serializable;

/**
 *
 * @author badi
 */
public class Maybe<A> implements Functor<Maybe, A>,
                                 From<Maybe, A>,
                                 Empty<Maybe, A>,
                                 Pointed<Maybe, A>,
                                 Applicative<Maybe, A>,
                                 Monad<Maybe, A>,
                                 Serializable
{
    private final A a;
    public final boolean isJust;

    private Maybe(A a){
        this.a = a;
        if(a == null)
            isJust = false;
        else isJust = true;
    }

    public static <A> Maybe<A> just(final A a){
        return new Maybe<A>(a);
    }

    public static <A> Maybe<A> nothing(){
        return new Maybe<A>(null);
    }

    public A fromJust(){
        return a;
    }

    public String toString(){
        return isJust ? "just(" + from() + ")" : "nothing()";
    }

    @Override
    public boolean equals(final Object o){
        if(o instanceof Maybe){
            Maybe m = (Maybe) o;
            return isJust && m.isJust && fromJust().equals(m.fromJust());
        } else return false;
    }

    public <B> Maybe<B> fmap(final F1<A, B> f) {
        if(isJust)
            return just(f.f((A) a));
        else return nothing();
    }

    public A from(){
        return fromJust();
    }

    public <A> Maybe<A> pure(A a) {
        return just(a);
    }

    public <B> Maybe<B> bind(final F1<A, Monad<Maybe, B>> f) {
        if(isJust)
            return (Maybe<B>) f.f(a);
        else return nothing();
    }


    public <B> Maybe<B> apply(From<Maybe, F1<A, B>> f) {
        return fmap(f.from());
    }

    public Empty<Maybe, A> empty() {
        return nothing();
    }
}
